package util

import (
	"archive/tar"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type TarFileInfo struct {
	Name  string
	Mode  uint32
	Size  int64
	IsDir bool
}

// List all files in archive.tar verbosely.
func ListTarFiles(fileSource string) ([]TarFileInfo, error) {
	tarFilesInfo := make([]TarFileInfo, 0)

	file, err := os.Open(fileSource)
	if err != nil {
		return nil, fmt.Errorf("listTarFiles - open '%s' error: %v", fileSource, err)
	}
	defer file.Close()
	tarReader := tar.NewReader(file)

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break //
		}
		if err != nil {
			return nil, fmt.Errorf("listTarFiles - next header error: %v", err)
		}

		fileInfo := new(TarFileInfo)
		fileInfo.Name = header.Name
		fileInfo.Mode = uint32(header.FileInfo().Mode())
		fileInfo.Size = header.Size
		fileInfo.IsDir = header.FileInfo().Mode().IsDir()

		tarFilesInfo = append(tarFilesInfo, *fileInfo)
	}

	return tarFilesInfo, nil
}

// https://www.cnblogs.com/binHome/p/12573737.html
func tarFile(filesource string, sfileInfo os.FileInfo, tarwriter *tar.Writer) error {
	sfile, err := os.Open(filesource)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer sfile.Close()
	header, err := tar.FileInfoHeader(sfileInfo, "")
	if err != nil {
		fmt.Println(err)
		return err
	}
	header.Name = filesource
	err = tarwriter.WriteHeader(header)
	if err != nil {
		fmt.Println(err)
		return err
	}
	if _, err = io.Copy(tarwriter, sfile); err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

func TarFolder(directory string, tarwriter *tar.Writer) error {
	return filepath.Walk(directory, func(targetpath string, file os.FileInfo, err error) error {
		//read the file failure
		if file == nil {
			return err
		}
		if file.IsDir() {
			if directory == targetpath {
				return nil
			}
			header, err := tar.FileInfoHeader(file, "")
			if err != nil {
				return err
			}
			header.Name = filepath.Join(directory, strings.TrimPrefix(targetpath, directory))
			if err = tarwriter.WriteHeader(header); err != nil {
				return err
			}
			os.Mkdir(strings.TrimPrefix(directory, file.Name()), os.ModeDir)

			return TarFolder(targetpath, tarwriter)
		}
		return tarFile(targetpath, file, tarwriter)
	})
}

func UntarFile2(tarFile string, untarPath string) error {
	fr, er := os.Open(tarFile)
	if er != nil {
		return er
	}
	defer fr.Close()
	//tr := tar.NewReader(fr)

	return Untar(fr, untarPath)
}

func UntarFile(tarFile string, untarPath string) error {
	fr, er := os.Open(tarFile)
	if er != nil {
		return er
	}
	defer fr.Close()
	// 创建 tar.Reader，准备执行解包操作
	tr := tar.NewReader(fr)
	//用 tr.Next() 来遍历包中的文件，然后将文件的数据保存到磁盘中
	for hdr, er := tr.Next(); er != io.EOF; hdr, er = tr.Next() {
		if er != nil {
			return er
		}
		//先创建目录
		fileName := untarPath + "/" + hdr.Name
		dir := path.Dir(fileName)
		_, err := os.Stat(dir)
		//如果err 为空说明文件夹已经存在，就不用创建
		if err != nil {
			err = os.MkdirAll(dir, os.ModePerm)
			if err != nil {
				fmt.Print(err)
				return err
			}
		}
		//获取文件信息
		fi := hdr.FileInfo()
		//创建空文件，准备写入解压后的数据
		fw, er := os.Create(fileName)
		if er != nil {
			return er
		}
		defer fw.Close()
		// 写入解压后的数据
		_, er = io.Copy(fw, tr)
		if er != nil {
			return er
		}
		// 设置文件权限
		os.Chmod(fileName, fi.Mode().Perm())
	}
	return nil
}

func ReadFileInTar(tarFileName, fileInTar string) ([]byte, error) {
	file, err := os.Open(tarFileName)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := tar.NewReader(file)
	for {
		header, err := reader.Next()
		if err == io.EOF {
			return []byte{}, nil
		}
		if err != nil {
			return nil, err
		}

		if header.Name == fileInTar {
			contentBytes, err := io.ReadAll(reader)
			if err != nil {
				return nil, err
			}
			return contentBytes, nil
		}
	}
}
